from abc import ABCMeta, abstractmethod

from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from selenium.webdriver.remote.webelement import WebElement

from Autotests.WebDriver import DRIVER

WebFindKey = By


class BaseControl(object):
    __metaclass__ = ABCMeta

    def __init__(self):
        self._timeoutSec = 1 * 10  # 10 sec

    def controlTimeout(self):
        return self._timeoutSec

    def setControlTimeout(self, timeoutSec):
        self._timeoutSec = timeoutSec

    @abstractmethod
    def isVisible(self):
        pass

    @abstractmethod
    def isEnabled(self):
        pass

    @abstractmethod
    def isExists(self, timeoutSec=None):
        pass

    @abstractmethod
    def click(self):
        pass


class BaseWebControlImpl(BaseControl):
    def __init__(self, webElement):
        super(BaseWebControlImpl, self).__init__()
        self._obj = webElement

    def obj(self):
        return self._obj

    def waitFor(self, callback, timeoutSec=None, message='', ignored_exceptions=None):
        timeout = timeoutSec if timeoutSec is not None else self.controlTimeout()
        return DRIVER().waitFor(callback, timeout, message, ignored_exceptions)

    def getChildControl(self, by, value):
        try:
            return BaseWebControlImpl(self.find_element(by, value))
        except NoSuchElementException:
            return None

    def getChildControls(self, by, value):
        try:
            return [BaseWebControlImpl(control) for control in self.find_elements(by, value)]
        except NoSuchElementException:
            return []

    def waitChildControl(self, by, value, timeoutSec = None, noneIfTimeout=False):
        try:
            errorMessage = "Child control %s not found for element %s" % (value, self.tag_name)
            return self.waitFor(lambda x: self.getChildControl(by, value), timeoutSec, errorMessage)
        except TimeoutException, e:
            if not noneIfTimeout:
                raise e

    def waitChildControls(self, by, value, timeoutSec = None, emptyIfTimeout=False):
        try:
            errorMessage = "Child control %s not found for element %s" % (value, self.tag_name)
            return self.waitFor(lambda x: self.getChildControls(by, value), timeoutSec, errorMessage)
        except TimeoutException, e:
            if not emptyIfTimeout:
                raise e
            return []

    def isVisible(self):
        return self.is_displayed()

    def isEnabled(self):
        return self.is_enabled()

    def isExists(self, timeoutSec=None):
        try:
            return bool(self.obj(timeoutSec))
        except Exception:
            return False

    def hasChilds(self, timeoutSec=0):
        return bool(self.waitChildControls(WebFindKey.XPATH, '*', timeoutSec, True))

    def click(self):
        DRIVER().actionChains().click(self.obj()).perform()

    def __getattr__(self, item):
        return getattr(self.obj(), item)


class BaseWebControl(BaseWebControlImpl):
    def __init__(self, controlHandler):
        if isinstance(controlHandler, (BaseWebControlImpl, WebElement)):
            self._super().__init__(controlHandler)
            self.obj = self._super().obj
        else:
            self._mapItem = controlHandler
            self._super().__init__(self)

    def _super(self):
        return super(BaseWebControl, self)

    def obj(self, timeoutSec=None):
        finder = lambda x: x.find_element(*self._mapItem)
        return self.waitFor(finder, timeoutSec, 'element %s not found' % str(self._mapItem))
